
#include <iostream>
#include "common.h"
#include "LfsrTests.h"
#include "Algebra.h"
#include "AlgebraFactory.h"
#include "test.h"
#include <sstream>
#include <fstream>

namespace LfsrTests
{

//TODO: generalize
template<class Category, int size>
void test(IAlgebra *algebra, int seqLength)
{
	Lfsr<Category, size>::ControlSequence c(0);
	c.at(0) = size;
	c.at(1) = size < 5 ? 1 : 2;
	c.at(2) = 0;
	Lfsr<Category, size> gen(algebra, c);
	BruteIterator<Category, size> it(Lfsr<Category, size>::Factorset(0), 2);
	std::ostringstream title;
	title << "Lfsr of size " << size;
	explorePeriods(&gen, &it, seqLength, title.str());
}

//TODO: generalize
template<class Category, int size>
Lfsr<Category, size>* findBestLfsr(IAlgebra *algebra, int seqLength)
{
	Lfsr<Category, size>::ControlSequence c(0);
	c.at(0) = size;
	c.at(1) = size < 5 ? 1 : 2;
	c.at(2) = 0;
	Lfsr<Category, size> *gen = new Lfsr<Category, size>(algebra, c);
	BruteIterator<Category, size> it(Lfsr<Category, size>::Factorset(0), 2);
	PeriodExplorer<SmallGenerator, BruteIterator<Category, size> > e;
	e.explore(gen, &it, seqLength);
	gen->setCongiguration(e.bestConfigurations().front());
	return gen;
}

typedef Lfsr<right_operator_tag, 2> SmallGenerator;

bool generate(IAlgebra *algebra, int seqLength, std::string fileName)
{
	std::cout << "looking for long period sequence...";
	SmallGenerator::ControlSequence c(0);
	c.at(0) = 2;
	c.at(1) = 1;
	c.at(2) = 0;
	SmallGenerator *gen = findBestLfsr<right_operator_tag, 2>(algebra, 100);
	std::cout << gen->getConfiguration().toString()
		<< std::endl << "generating sequence..." << std::endl;
	std::ofstream f(fileName.c_str(), std::ios_base::binary);
	if(!f)
	{
		std::cerr << "Failed to open file \"" << fileName << "\" for writing";
		return false;
	}
	else
	{
		for(int i = 0; i < seqLength + (4 - (seqLength % 4)) % 4; i++)
		{
			if(i % SmallGenerator::State::dataSize == 0)
			{
				f.write(gen->next(), SmallGenerator::State::dataSize);
			}
			else
			{
				gen->next();
			}
		}
	}
	return true;
}

void defaultTest(IAlgebra *algebra)
{
	std::cout << std::endl << ".:[Lfsr tests]:." << std::endl  << std::endl << "Left multiplication." << std::endl;
	test<left_operator_tag, 2>(algebra, 50);
	test<left_operator_tag, 3>(algebra, 300);
	test<left_operator_tag, 4>(algebra, 4000);
	test<left_operator_tag, 5>(algebra, 30000);
	std::cout << std::endl << "Right multiplication." << std::endl;
	test<right_operator_tag, 2>(algebra, 100);
	test<right_operator_tag, 3>(algebra, 3000);
	test<right_operator_tag, 4>(algebra, 16000);
	test<right_operator_tag, 5>(algebra, 320000);
}

}